package com.hw.saweb.config;

import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.filter.SaServletFilter;
import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.jwt.StpLogicJwtForSimple;
import cn.dev33.satoken.router.SaHttpMethod;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.annotation.Resource;

/**
 * @program: sa-token-start
 * @author: hewei
 * @date: 2023-09-05 18:47
 * sa-token 基于 SaInterceptor 拦截器的 controller 层权限校验配置
 *
 **/

@Slf4j
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {

    @Resource
    CorsInterceptor corsInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(corsInterceptor).addPathPatterns("/**");
        // 注册 Sa-Token 拦截器，定义详细认证规则
        registry.addInterceptor(new SaInterceptor(handler -> {
            // 指定一条 match 规则
            SaRouter
                    .match("/**")    // 拦截的 path 列表，可以写多个 */
                    .notMatch("/user/doLogin","/user/logout","/sign/**","/favicon.ico")        // 排除掉的 path 列表，可以写多个
//                    .notMatch("/user/doLogin","/user/logout","/**/*.js","/**/*.css")        // 排除掉的 path 列表，可以写多个
                    .check(r -> StpUtil.checkLogin());        // 要执行的校验动作，可以写完整的 lambda 表达式

//            SaRouter.match("/**", "/user/doLogin",r -> StpUtil.checkLogin());
            // 根据路由划分模块，不同模块不同鉴权
            SaRouter
                    .match("/user/**")
                    .notMatch("/user/doLogin")
                    .check(r -> StpUtil.checkRole("user"));
            SaRouter.match("/session/**", r -> StpUtil.checkRole("session"))
                    .notMatch("/session/anonSession")
                    .check(r -> StpUtil.checkRole("session"));
            SaRouter.match("/annotation/**", r -> StpUtil.checkRole("annotation"));
            SaRouter.match("/admin/**", r -> StpUtil.checkPermission("admin"));
            SaRouter.match("/goods/**", r -> StpUtil.checkPermission("goods"));
            SaRouter.match("/orders/**", r -> StpUtil.checkPermission("orders"));
            SaRouter.match("/comment/**", r -> StpUtil.checkPermission("comment"));

            // 根据请求类型匹配
//            SaRouter.match(SaHttpMethod.GET).check( r-> StpUtil.checkPermission("user.get"));

            /*多个条件一起使用,功能说明: 必须是 Get 请求 并且 请求路径以 `/service/` 开头*/
            SaRouter.match(SaHttpMethod.GET).match("/service/**").check( r -> {
                StpUtil.checkRole("service");
                StpUtil.checkPermissionAnd("user.get","user.add");
            } );

        })
        /*
        使用拦截器模式，只能在Controller层进行注解鉴权。
        指定关闭掉拦截器默认的注解鉴权能力，这样框架就只会做路由拦截校验了。
        关闭后可以引入 sa-token-spring-aop 依赖，实现全层次注解鉴权，也不会和拦截器模式的鉴权重复
        */
//        .isAnnotation(false)
        ).addPathPatterns("/**")
         .excludePathPatterns("/**/*.js","/**/*.css","/user/doLogin","/user/logout","/sign/**","/favicon.ico","/actuator/**");
    }


    /**
     * 注册 [Sa-Token全局过滤器]
     *
     * 在 webFlux 中 通过 SaReactorFilter 过滤器实现
     * @Bean
     * public SaReactorFilter getSaReactorFilter() {
     *     return new SaReactorFilter()
     *         // 其它代码...
     *     ;
     * }
     */
    @Bean
    public SaServletFilter getSaServletFilter() {
        return new SaServletFilter()

                // 指定 拦截路由 与 放行路由
                .addInclude("/**").addExclude("/favicon.ico","/**/*.js","/**/*.css","/user/doLogin","/user/logout","/sign/**","favicon.ico","/actuator/**")

                // 认证函数: 每次请求执行
                .setAuth(obj -> {
                    log.info("SaServlet>>> 进入Sa-Token全局认证，请求源地址：{}",SaHolder.getRequest().getUrl());
//                    SaRouter.match("/**").notMatch("/user/doLogin","/user/logout").check(r -> StpUtil.checkLogin());
                })

                // 异常处理函数：每次认证函数发生异常时执行此函数
                .setError(e -> {
                    log.info("SaServlet>>> 进入Sa-Token异常处理，请求源地址：{}",SaHolder.getRequest().getUrl());
                    return SaResult.error(e.getMessage());
                    // 使用封装的 JSON 工具类转换数据格式
//                    return JSON.toJsonStr( SaResult.error(e.getMessage()) );
                })

                // 前置函数：在每次认证函数之前执行（BeforeAuth 不受 includeList 与 excludeList 的限制，所有请求都会进入）
                .setBeforeAuth(r -> {
                    // ---------- 设置一些安全响应头 ----------
                    SaHolder.getResponse()
                            // 服务器名称
                            .setServer("sa-server")
                            // 是否可以在iframe显示视图： DENY=不可以 | SAMEORIGIN=同域下可以 | ALLOW-FROM uri=指定域名下可以
                            .setHeader("X-Frame-Options", "SAMEORIGIN")
                            // 是否启用浏览器默认XSS防护： 0=禁用 | 1=启用 | 1; mode=block 启用, 并在检查到XSS攻击时，停止渲染页面
                            .setHeader("X-XSS-Protection", "1; mode=block")
                            // 禁用浏览器内容嗅探
                            .setHeader("X-Content-Type-Options", "nosniff");
                });
    }

    // Sa-Token 整合 jwt (Simple 简单模式)
    @Bean
    public StpLogic getStpLogicJwt() {
        return new StpLogicJwtForSimple();
    }

    /**
     * 重写 Sa-Token 框架内部token生成算法策略，建议使用默认UUID，防止token重复，
     * 如果集成了 sa-token-jwt 则此处失效
     */
//    @Autowired
//    public void rewriteSaStrategy() {
//        // 重写 Token 生成策略
//        SaStrategy.instance.createToken = (loginId, loginType) -> {
//            return SaFoxUtil.getRandomString(32);    // 随机32位长度字符串
//        };
//    }

//    注册sa-token事件监听器
//    @PostConstruct
//    public void init() {
//        SaTokenEventCenter.registerListener(new MySaTokenListener());
//    }

}
